home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / Calendar.java < prev    next >
Text File  |  1998-09-04  |  25KB  |  851 lines

  1. package symantec.itools.awt.util;
  2.  
  3. import java.awt.Panel;
  4. import java.awt.Dimension;
  5. import java.awt.Color;
  6. import java.awt.Font;
  7. import java.awt.Rectangle;
  8. import java.awt.Graphics;
  9. import java.awt.FontMetrics;
  10. import java.awt.Container;
  11. import java.util.Date;
  12. import java.text.DateFormat;
  13. import symantec.itools.awt.ComboBox;
  14. import symantec.itools.awt.util.spinner.NumericSpinner;
  15. import java.beans.PropertyVetoException;
  16. import java.beans.PropertyChangeListener;
  17. import java.beans.VetoableChangeListener;
  18. import java.awt.event.MouseEvent;
  19. import java.awt.event.ActionEvent;
  20. import java.awt.event.ActionListener;
  21. import java.awt.AWTEvent;
  22. import java.awt.AWTEventMulticaster;
  23. import java.awt.LayoutManager;
  24.  
  25. //    01/29/97    TWB    Integrated changes from Windows
  26. //    01/30/97    RKM    Changed invalidates to repaint
  27. //                    Changed logic in paint
  28. //                        1)sc.setBackground is called, only if the colors are different
  29. //                        2)sc.paint is called instead of repaint
  30. //                    Made setDate actually work
  31. //    06/05/97    LAB    Updated to Java 1.1.  Changed most data members to protected, from private.
  32. //    07/14/97    RKM    Added empty override to setLayout, so users could not change it on us
  33. //    07/18/97    LAB    Added add/removeNotify to handle event listener registration.
  34. //                    Updated version number to 1.1.
  35. //  08/25/97    CAR getDate now returns a nicely formatted string
  36. //  09/14/97    RKM Removed hard coded month strings
  37. //  10/03/97    LAB Made paint use ComboBox's preferred size.  This was preventing the ComboBox from
  38. //                    displaying properly (Addresses Mac Bug #7491).  Reworked the way the component
  39. //                    lays its self out and paints itself in an attempt to make it less hard coded
  40. //                    and more dynamic.  Made paint use NumericSpinner's preferred size.  This did
  41. //                    away with the need for resize to be overridden.
  42.  
  43. /**
  44.  * Calendar component.  Creates a graphic calendar displaying a month of dates.
  45.  * A combo box controls the month displayed, a numeric spin control changes the
  46.  * year displayed.
  47.  *
  48.  *
  49.  * @version 1.1, July 18, 1997
  50.  *
  51.  * @author  Symantec
  52.  *
  53.  */
  54. public class Calendar extends Panel
  55. {
  56.     /**
  57.      * Creates a default calendar with today's
  58.      * date initialized.
  59.      */
  60.     public Calendar()
  61.     {
  62.         this(new Date());
  63.     }
  64.  
  65.     /**
  66.      * Creates a calendar with the specified date initialized.
  67.      * @param defaultdate the date to be shown initially
  68.      */
  69.     public Calendar(Date defaultdate)
  70.     {
  71.         super.setLayout(null);
  72.  
  73.         bNeedsPlatformHelp = ComboBox.needsPlatformHelp();  // help comboBar change state
  74.  
  75.         dCurrent = defaultdate;
  76.         dLast = dCurrent;
  77.         combo = new ComboBox();
  78.         try
  79.         {
  80.             java.text.DateFormatSymbols dfs = new java.text.DateFormatSymbols();
  81.             String months[] = dfs.getMonths();
  82.  
  83.             //??? LAB ??? The last item is blank, why?
  84.             int length = months.length > 12 ? 12 : months.length;
  85.  
  86.             for(int i = 0; i < length; ++i)
  87.                 combo.addItem(months[i]);
  88.         }
  89.         catch(PropertyVetoException e) {}
  90.         add(combo);
  91.         sc = new NumericSpinner();
  92.         add(sc);
  93.  
  94.         try
  95.         {
  96.             sc.setMin(1900);
  97.             sc.setMax(9999);
  98.             sc.setCurrent(1900 + dCurrent.getYear());
  99.             sc.setEditable(false);
  100.             combo.select( 0 );
  101.             combo.select(dCurrent.getMonth());
  102.         }
  103.         catch(PropertyVetoException e) {}
  104.  
  105.         if (symantec.itools.lang.OS.isMacintosh())
  106.             selectedColor = Color.black;
  107.         else
  108.             selectedColor = Color.blue;
  109.  
  110.         if (!symantec.itools.lang.OS.isMacintosh())  //12/18/96  Andy McFarland some platforms don't want their fonts hardwired...
  111.         {
  112.             if (bNeedsPlatformHelp)  // different fonts to help display problems.
  113.                 setFont(new Font("Dialog", Font.PLAIN, 9));
  114.             else
  115.                 setFont(new Font("Dialog", Font.BOLD, 10));
  116.         }
  117.     }
  118.  
  119.     /**
  120.      * Sets the date selected on the calendar.
  121.      * @param date the date that the calendar is to be set to
  122.      *
  123.      * @exception PropertyVetoException
  124.      * if the specified property value is unacceptable
  125.      */
  126.     public void setDate(String date) throws PropertyVetoException
  127.     {
  128.         String oldValue = getDate();
  129.  
  130.         if(!symantec.itools.util.GeneralUtils.objectsEqual(oldValue, date))
  131.         {
  132.             vetos.fireVetoableChange("Date", oldValue, date);
  133.  
  134.             //Convert string to Date
  135.             Date newDate;
  136.             try {
  137.                 newDate = new Date(date);
  138.             } catch (Exception e) {
  139.                 newDate = new Date();
  140.             }
  141.  
  142.             if (!dCurrent.equals(newDate))
  143.             {
  144.                 dCurrent = newDate;
  145.  
  146.                 combo.select(dCurrent.getMonth());
  147.                 sc.setCurrent(1900 + dCurrent.getYear());
  148.  
  149.                 //Trigger re calculation
  150.                 firstDay = -1;
  151.                 lastSelectedDate = -1;
  152.  
  153.                 repaint();
  154.             }
  155.             changes.firePropertyChange("Date", oldValue, date);
  156.         }
  157.     }
  158.  
  159.     /**
  160.      * Returns the date that the calendar is set to.
  161.      * @return the current value of the calendar.
  162.      */
  163.     public String getDate()
  164.     {
  165.         String str;
  166.         DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
  167.         str = df.format(dCurrent);
  168.         return str;
  169.     }
  170.  
  171.     /**
  172.      * Sets the color used to highlight the selected date on the calendar.
  173.      * @param c the color that the selected color is to be set to
  174.      *
  175.      * @exception PropertyVetoException
  176.      * if the specified property value is unacceptable
  177.      */
  178.     public void setSelectedColor(Color c) throws PropertyVetoException
  179.     {
  180.         Color oldValue = selectedColor;
  181.  
  182.         if(!symantec.itools.util.GeneralUtils.objectsEqual(oldValue, c))
  183.         {
  184.             vetos.fireVetoableChange("SelectedColor", oldValue, c);
  185.  
  186.             selectedColor = c;
  187.             repaint();
  188.  
  189.             changes.firePropertyChange("SelectedColor", oldValue, c);
  190.         }
  191.     }
  192.  
  193.     /**
  194.      * Returns the color that highlights the selected date on the calendar.
  195.      * @return the current selected color.
  196.      */
  197.     public Color getSelectedColor()
  198.     {
  199.         return selectedColor;
  200.     }
  201.  
  202.     /**
  203.      * Tells this component that it has been added to a container.
  204.      * This is a standard Java AWT method which gets called by the AWT when
  205.      * this component is added to a container. Typically, it is used to
  206.      * create this component's peer.
  207.      *
  208.      * It has been overridden here to hook-up event listeners.
  209.      *
  210.      * @see #removeNotify
  211.      */
  212.     public synchronized void addNotify()
  213.     {
  214.         super.addNotify();
  215.  
  216.         //Hook up listeners
  217.         if (mouse == null)
  218.         {
  219.             mouse = new Mouse();
  220.             addMouseListener(mouse);
  221.         }
  222.         if (action == null)
  223.         {
  224.             action = new Action();
  225.             sc.addActionListener(action);
  226.             combo.addActionListener(action);
  227.         }
  228.     }
  229.  
  230.     /**
  231.      * Tells this component that it is being removed from a container.
  232.      * This is a standard Java AWT method which gets called by the AWT when
  233.      * this component is removed from a container. Typically, it is used to
  234.      * destroy the peers of this component and all its subcomponents.
  235.      *
  236.      * It has been overridden here to unhook event listeners.
  237.      *
  238.      * @see #addNotify
  239.      */
  240.     public synchronized void removeNotify()
  241.     {
  242.         //Unhook listeners
  243.         if (mouse != null)
  244.         {
  245.             removeMouseListener(mouse);
  246.             mouse = null;
  247.         }
  248.         if (action != null)
  249.         {
  250.             sc.removeActionListener(action);
  251.             combo.removeActionListener(action);
  252.             action = null;
  253.         }
  254.  
  255.         super.removeNotify();
  256.     }
  257.  
  258.     /**
  259.      * Handles redrawing of this component on the screen.
  260.      * This is a standard Java AWT method which gets called by the Java
  261.      * AWT (repaint()) to handle repainting this component on the screen.
  262.      * The graphics context clipping region is set to the bounding rectangle
  263.      * of this component and its <0,0> coordinate is this component's
  264.      * top-left corner.
  265.      * Typically this method paints the background color to clear the
  266.      * component's drawing space, sets graphics context to be the foreground
  267.      * color, and then calls paint() to draw the component.
  268.      *
  269.      * It is overridden here to reduce flicker by eliminating the uneeded
  270.      * clearing of the background.
  271.      *
  272.      * @param g the graphics context
  273.      * @see java.awt.Component#repaint
  274.      * @see #paint
  275.      */
  276.     public void update(Graphics g)
  277.     {
  278.         paint(g);
  279.     }
  280.  
  281.     /**
  282.      * Paints this component using the given graphics context.
  283.      * This is a standard Java AWT method which typically gets called
  284.      * by the AWT to handle painting this component. It paints this component
  285.      * using the given graphics context. The graphics context clipping region
  286.      * is set to the bounding rectangle of this component and its <0,0>
  287.      * coordinate is this component's top-left corner.
  288.      *
  289.      * @param g the graphics context used for painting
  290.      * @see java.awt.Component#repaint
  291.      * @see #update
  292.      */
  293.     public void paint(Graphics g)
  294.     {
  295.         //If the background of the sc is different, set it
  296.         if (!sc.getBackground().equals(getBackground()))
  297.             sc.setBackground(getBackground());
  298.  
  299.         Dimension spinnerSize = sc.getPreferredSize();
  300.         sc.setBounds(getSize().width - spinnerSize.width - edgePad, edgePad, spinnerSize.width, spinnerSize.height);
  301.  
  302.         FontMetrics fm = getFontMetrics(getFont());
  303.  
  304.         Rectangle r = bounds();
  305.  
  306.         if (bNeedsPlatformHelp)
  307.         {   // Solaris parameters
  308.             combo.reshape(12, 20, 100, 34);
  309.             //sc.reshape(r.width - 90, 20, 80, 34);
  310.             topRow = 70;
  311.             selectHeight = fm.getHeight() + 2;
  312.             centerNumber = 14;
  313.             heightAdjust = 6;
  314.             numberAdjust = 6;
  315.         }
  316.         else    //Macintosh and Windows parameters
  317.         {
  318.             Dimension comboSize = combo.getPreferredSize();
  319.             combo.setBounds(edgePad, edgePad, comboSize.width, comboSize.height);
  320.             topRow = spinnerSize.height + edgePad + 2;
  321.             selectHeight = fm.getHeight();
  322.             centerNumber = 14;
  323.             heightAdjust = 2;
  324.             numberAdjust = 0;
  325.         }
  326.  
  327.         int widthWith2EdgeAdj = r.width - (2 * edgePad);
  328.  
  329.         int blockwidth = widthWith2EdgeAdj / 7;            // 7 columns
  330.         int blockheight = ((r.height - topRow - edgePad) / 7);    // 7 rows
  331.         int halfBlockwidth = blockwidth / 2;
  332.         int xloc = halfBlockwidth  - (fm.stringWidth("S") / 2);        //average location in block
  333.         int yloc = topRow + centerNumber;
  334.         if (dateSelectedx < 0)
  335.         {
  336.             int widthWithEdgeAdj = r.width - edgePad;
  337.             int topRowBlockHeight = topRow + (7 * blockheight);
  338.  
  339.             // top
  340.             g.setColor(Color.black);
  341.             g.drawLine(edgePad, topRow, r.width - edgePad - 2, topRow);
  342.  
  343.             // bottom
  344.             g.setColor(Color.lightGray);
  345.             g.drawLine(edgePad, topRowBlockHeight, widthWithEdgeAdj - 3, topRowBlockHeight);
  346.             g.setColor(Color.white);
  347.             g.drawLine(edgePad + 1, topRowBlockHeight + 1, widthWithEdgeAdj - 2, topRowBlockHeight + 1);
  348.  
  349.             // left
  350.             g.setColor(Color.black);
  351.             g.drawLine(edgePad, topRow, edgePad, topRowBlockHeight);
  352.  
  353.             // right
  354.             g.setColor(Color.lightGray);
  355.             g.drawLine(widthWithEdgeAdj - 2, topRow, widthWithEdgeAdj - 2, topRowBlockHeight);
  356.             g.setColor(Color.white);
  357.             g.drawLine(widthWithEdgeAdj - 1, topRow, widthWithEdgeAdj - 1, topRowBlockHeight);
  358.  
  359.             // fill box
  360.             g.fillRect(edgePad + 1, topRow + blockheight , widthWith2EdgeAdj - 3, (6 * blockheight) );
  361.             g.setColor(Color.gray);
  362.             g.fillRect(edgePad + 1, topRow + 1, widthWith2EdgeAdj - 3, blockheight + 1 );
  363.  
  364.             // letters on top
  365.             g.setColor(Color.white);
  366.  
  367.             if (symantec.itools.lang.OS.isMacintosh())
  368.             {
  369.                 yloc += (blockheight+1)/2 - fm.getHeight()/2; // Lets have the day labels track resizes
  370.                                                            // in the component like the numbers do...
  371.             }
  372.  
  373.             g.drawString("S", xloc, yloc - 1);
  374.             xloc = blockwidth + (halfBlockwidth - (fm.stringWidth("M") / 2));
  375.             g.drawString("M", xloc, yloc - 1);
  376.             xloc = (blockwidth * 2) + (halfBlockwidth - (fm.stringWidth("T") / 2));
  377.             g.drawString("T", xloc, yloc - 1);
  378.             xloc = (blockwidth * 3) + (halfBlockwidth - (fm.stringWidth("W") / 2));
  379.             g.drawString("W", xloc, yloc - 1);
  380.             xloc = (blockwidth * 4) + (halfBlockwidth - (fm.stringWidth("T") / 2));
  381.             g.drawString("T", xloc, yloc - 1);
  382.             xloc = (blockwidth * 5) + (halfBlockwidth - (fm.stringWidth("FM") / 2));
  383.             g.drawString("F", xloc, yloc - 1);
  384.             xloc = (blockwidth * 6) + (halfBlockwidth - (fm.stringWidth("S") / 2));
  385.             g.drawString("S", xloc, yloc - 1);
  386.         }
  387.  
  388.         Date dMonthStart;
  389.         // must be run twice for Solaris
  390.         dMonthStart = new Date(dCurrent.getYear(), dCurrent.getMonth(), 1);
  391.         dMonthStart = new Date(dCurrent.getYear(), dCurrent.getMonth(), 1);
  392.  
  393.         // which date does month begin?
  394.         if (firstDay < 0)
  395.             firstDay = dMonthStart.getDay();
  396.         int initdate = -1;
  397.         int caldate = 1;
  398.         int templastdate = -1;
  399.         int month = dCurrent.getMonth() + 1;
  400.  
  401.         // which date already selected
  402.         if (lastSelectedDate < 1) initdate = dCurrent.getDate();
  403.         else if (dateSelectedx < 0) initdate = lastSelectedDate;
  404.  
  405.         // paint calendar
  406.         g.setColor(Color.black);
  407.  
  408.     calendar:
  409.  
  410.         for (int j = 0; j < 6; j++)
  411.         {
  412.             for (int i = 0; i < 7; i++)
  413.             {
  414.                 if ((maxMonthy < dateSelectedy) || (maxMonthy == dateSelectedy && maxMonthx < dateSelectedx))
  415.                 {   //click after end date
  416.                     dateSelectedx = -1;
  417.                     dateSelectedy = -1;
  418.                     break calendar;
  419.                  }
  420.                 if (j == 0 && i < firstDay)
  421.                 {   // click before the start day
  422.                     if (dateSelectedx == i && dateSelectedy == j)
  423.                     {
  424.                         dateSelectedx = -1;
  425.                         dateSelectedy = -1;
  426.                         break calendar;
  427.                     }
  428.                     else
  429.                     continue;
  430.                 }
  431.  
  432.                 // calculate where to draw
  433.                 xloc = (i * blockwidth) + (halfBlockwidth - (fm.stringWidth(Integer.toString(caldate)) / 2));
  434.                 yloc = topRow + heightAdjust + 4 + numberAdjust + (((j+2) * blockheight) - fm.getHeight());
  435.  
  436.                 // draw one digit numbers differently from two digit
  437.                 if (dateSelectedx < 0 && initdate != caldate)
  438.                 {
  439.                     if (caldate < 10)
  440.                         g.drawString((Integer.toString(caldate)), xloc , yloc);
  441.                     else
  442.                         g.drawString(Integer.toString(caldate), xloc , yloc);
  443.                 }
  444.                 else if (dateSelectedx == i && dateSelectedy == j || initdate == caldate)
  445.                 {
  446.                     if (dateSelectedx == i && dateSelectedy == j)
  447.                     {
  448.                         int tempYear = dCurrent.getYear();
  449.                         int tempMonth = dCurrent.getMonth();
  450.                         // must be run twice for Solaris
  451.                         dCurrent = new Date();
  452.                         dCurrent = new Date();
  453.                         dCurrent.setDate(caldate);
  454.                         dCurrent.setYear(tempYear);
  455.                         dCurrent.setMonth(tempMonth);
  456.                     }
  457.                     //draw selected number
  458.                     g.setColor(selectedColor);
  459.                     if (caldate < 10)
  460.                         g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)) + 2, selectHeight);
  461.                     else
  462.                         g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)), selectHeight);
  463.                     g.setColor(Color.white);
  464.                     if (caldate < 10)
  465.                         g.drawString(Integer.toString(caldate), xloc , yloc);
  466.                     else
  467.                         g.drawString(Integer.toString(caldate), xloc , yloc);
  468.                     templastdate = caldate;
  469.                     g.setColor(Color.black);
  470.                 }
  471.                 else if (caldate == lastSelectedDate)
  472.                 {   // unselected previously selected number
  473.                     g.setColor(Color.white);
  474.                     if (caldate < 10)
  475.                         g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)) + 2, selectHeight);
  476.                     else
  477.                         g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)), selectHeight);
  478.                     g.setColor(Color.black);
  479.                     if (caldate < 10)
  480.                         g.drawString(Integer.toString(caldate), xloc , yloc);
  481.                     else
  482.                         g.drawString(Integer.toString(lastSelectedDate), xloc, yloc);
  483.                     lastSelectedDate = -1;
  484.                 }
  485.                 if (caldate >= 31)
  486.                 {
  487.                     maxMonthx = i;
  488.                     maxMonthy = j;
  489.                     break calendar;
  490.                 }
  491.                 else if ((caldate >= 30) && ((month == 4) || (month == 6) || (month == 9) || (month == 11)))
  492.                 {
  493.                     maxMonthx = i;
  494.                     maxMonthy = j;
  495.                     break calendar;
  496.                 }
  497.                 else if ((caldate >= 29) && (month == 2))
  498.                 {
  499.                     maxMonthx = i;
  500.                     maxMonthy = j;
  501.                     break calendar;
  502.                 }
  503.                 else if ((caldate >= 28) && (month == 2) && (!isLeapYear(dCurrent.getYear())))
  504.                 {
  505.                     maxMonthx = i;
  506.                     maxMonthy = j;
  507.                     break calendar;
  508.                 }
  509.                 caldate++;
  510.             }
  511.         }
  512.  
  513.         if (lastSelectedDate < 0)
  514.             lastSelectedDate = templastdate;
  515.  
  516.         dateSelectedx = -1;
  517.         dateSelectedy = -1;
  518.  
  519.         if (!dLast.equals(dCurrent))
  520.         {
  521.             dLast = dCurrent;
  522.             sourceActionEvent();
  523.         }
  524.     }
  525.  
  526.     /**
  527.      * Returns the recommended dimensions to properly display this component.
  528.      * This is a standard Java AWT method which gets called to determine
  529.      * the recommended size of this component.
  530.      *
  531.      * @return the preferred dimensions for the calender (250 x 200 pixels).
  532.      *
  533.      * @see #minimumSize
  534.      */
  535.     public Dimension preferredSize()
  536.     {
  537.         return (new Dimension(250,200));
  538.     }
  539.  
  540.     /**
  541.      * Returns the minimum dimensions to properly display this component.
  542.      * This is a standard Java AWT method which gets called to determine
  543.      * the minimum size of this component.
  544.      *
  545.      * @return the minimum dimensions needed for the calender (250 x 200 pixels).
  546.      *
  547.      * @see #preferredSize
  548.      */
  549.     public Dimension minimumSize()
  550.     {
  551.         return (new Dimension(250,200));
  552.     }
  553.  
  554.     /**
  555.      * Adds a listener for all property change events.
  556.      * @param listener the listener to add
  557.      * @see #removePropertyChangeListener
  558.      */
  559.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
  560.     {
  561.         changes.addPropertyChangeListener(listener);
  562.     }
  563.  
  564.     /**
  565.      * Removes a listener for all property change events.
  566.      * @param listener the listener to remove
  567.      * @see #addPropertyChangeListener
  568.      */
  569.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
  570.     {
  571.         changes.removePropertyChangeListener(listener);
  572.     }
  573.  
  574.     /**
  575.      * Adds a listener for all vetoable property change events.
  576.      * @param listener the listener to add
  577.      * @see #removeVetoableChangeListener
  578.      */
  579.     public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
  580.     {
  581.         vetos.addVetoableChangeListener(listener);
  582.     }
  583.  
  584.     /**
  585.      * Removes a listener for all vetoable property change events.
  586.      * @param listener the listener to remove
  587.      * @see #addVetoableChangeListener
  588.      */
  589.     public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
  590.     {
  591.         vetos.removeVetoableChangeListener(listener);
  592.     }
  593.  
  594.     /**
  595.      * Sets the command name of the action event fired by this button.
  596.      * @param command The name of the action event command fired by this button
  597.      *
  598.      * @exception PropertyVetoException
  599.      * if the specified property value is unacceptable
  600.      */
  601.     public void setActionCommand(String command) throws PropertyVetoException
  602.     {
  603.         String oldValue = actionCommand;
  604.  
  605.         vetos.fireVetoableChange("ActionCommand", oldValue, command);
  606.         actionCommand = command;
  607.         changes.firePropertyChange("ActionCommand", oldValue, command);
  608.     }
  609.  
  610.     /**
  611.      * @return the command name of the action event fired by this button.
  612.      */
  613.     public String getActionCommand()
  614.     {
  615.         return actionCommand;
  616.     }
  617.  
  618.     /**
  619.      * Adds the specified action listener to receive action events
  620.      * from this button.
  621.      * @param l the action listener
  622.      */
  623.     public synchronized void addActionListener(ActionListener l)
  624.     {
  625.         actionListener = AWTEventMulticaster.add(actionListener, l);
  626.     }
  627.  
  628.     /**
  629.      * Removes the specified action listener so it no longer receives
  630.      * action events from this button.
  631.      * @param l the action listener
  632.      */
  633.     public synchronized void removeActionListener(ActionListener l)
  634.     {
  635.         actionListener = AWTEventMulticaster.remove(actionListener, l);
  636.     }
  637.  
  638.     /**
  639.      * Fire an action event to the listeners
  640.      */
  641.     public void sourceActionEvent()
  642.     {
  643.         if (actionListener != null)
  644.             actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand));
  645.     }
  646.  
  647.     /**
  648.      * This is the Mouse Event handling innerclass.
  649.      */
  650.     class Mouse extends java.awt.event.MouseAdapter
  651.     {
  652.         /**
  653.          * Handles Mouse Pressed events
  654.          * @param e the MouseEvent
  655.          */
  656.         synchronized public void mousePressed(MouseEvent e)
  657.         {
  658.             int topRow = -1;
  659.             int x = e.getX();
  660.             int y = e.getY();
  661.             if (bNeedsPlatformHelp) // Solaris machines have different drawing levels
  662.             {
  663.                 topRow = 70;
  664.                 centerNumber = 14;
  665.                 heightAdjust = 6;
  666.                 numberAdjust = 6;
  667.             }
  668.             else
  669.             {
  670.                 topRow = 40;        // needed for Windows drawing
  671.                 numberAdjust = 0;
  672.             }
  673.             Rectangle r = bounds();
  674.             int blockheight = ((r.height - topRow - 14) / 7);   // 7 rows
  675.             int blockwidth = (r.width - 32) / 7;                // 7 columns
  676.  
  677.             if ( ((x >= 15) && (x < r.width - 15)) &&   // if event in calendar window
  678.                  ((y >= topRow + blockheight) && (y < r.height - 14)))
  679.             {
  680.                 dateSelectedx = ((x - 16) / blockwidth);
  681.                 dateSelectedy = ((y  - topRow - blockheight) / blockheight);
  682.                 repaint();
  683.                 firstDay = -1;
  684.             }
  685.         }
  686.     }
  687.  
  688.     /**
  689.      * This is the Adjustment Event handling innerclass.
  690.      */
  691.     class Action implements java.awt.event.ActionListener
  692.     {
  693.         /**
  694.          * Handles Action events
  695.          * @param e the ActionEvent
  696.          */
  697.         public void actionPerformed(ActionEvent e)
  698.         {
  699.             Object target = e.getSource();
  700.  
  701.             // ComboBox list items == month type events
  702.             if (target instanceof ComboBox)
  703.             {
  704.                 int dy = dCurrent.getDate();
  705.                 int mo = combo.getSelectedIndex();
  706.                 int yr = dCurrent.getYear();
  707.                 dCurrent = new Date(yr, mo, dy);
  708.                 while (dCurrent.getMonth() != mo)
  709.                 {
  710.                     dCurrent = new Date(yr, mo, --dy);
  711.                     lastSelectedDate = -1;
  712.                 }
  713.                 firstDay = -1;
  714.                 repaint();
  715.             }
  716.  
  717.             // Spin Control list items == year type events
  718.             if (target == sc)
  719.             {
  720.                 int dy = dCurrent.getDate();
  721.                 int mo = dCurrent.getMonth();
  722.                 int yr = sc.getCurrent() - 1900;
  723.                 dCurrent = new Date(yr, mo, dy);
  724.                 while (dCurrent.getMonth() != mo)
  725.                 {
  726.                     dCurrent = new Date(yr, mo, --dy);
  727.                     lastSelectedDate = -1;
  728.                 }
  729.                 firstDay = -1;
  730.                 repaint();
  731.             }
  732.         }
  733.     }
  734.  
  735.     /**
  736.      * Takes no action.
  737.      * This is a standard Java AWT method which gets called to specify
  738.      * which layout manager should be used to layout the components in
  739.      * standard containers.
  740.      *
  741.      * Since layout managers CANNOT BE USED with this container the standard
  742.      * setLayout has been OVERRIDDEN for this container and does nothing.
  743.      *
  744.      * @param lm the layout manager to use to layout this container's components
  745.      * (IGNORED)
  746.      * @see java.awt.Container#getLayout
  747.      **/
  748.     public void setLayout(LayoutManager lm)
  749.     {
  750.     }
  751.  
  752.     boolean isLeapYear(int year)
  753.     {
  754.         if (year% 4 == 0 && (year != 2100))
  755.             return true;
  756.         else
  757.             return false;
  758.     }
  759.  
  760.     String actionCommand;
  761.     ActionListener actionListener = null;
  762.  
  763.     /**
  764.      * The ComboBox that displays/controls the month.
  765.      */
  766.     protected ComboBox combo;
  767.     /**
  768.      * True if the ComboBox needs help changing state.
  769.      * @see #combo
  770.      * @see symantec.itools.awt.ComboBox#needsPlatformHelp
  771.      */
  772.     protected boolean bNeedsPlatformHelp;
  773.     /**
  774.      * The spinner that displays/controls the year.
  775.      */
  776.     protected NumericSpinner sc;
  777.     /**
  778.      * The date that the calendar is set to.
  779.      * @see #setDate
  780.      * @see #getDate
  781.      */
  782.     protected Date dCurrent;
  783.     /**
  784.      * The current date when the Calendar was last painted.
  785.      */
  786.     protected Date dLast;
  787.     /**
  788.      * The color used to highlight the selected date on the calendar.
  789.      * @see #setSelectedColor
  790.      * @see #getSelectedColor
  791.      */
  792.     protected Color selectedColor;
  793.     /**
  794.      * Not used.
  795.      */
  796.     protected String cal[][];
  797.     /**
  798.      * The number of pixels to offset the internal components from the edges.
  799.      */
  800.     protected int edgePad = 2;
  801.     /**
  802.      * The column index of the currently selected day.
  803.      */
  804.     protected int dateSelectedx = -1;
  805.     /**
  806.      * The row index of the currently selected day.
  807.      */
  808.     protected int dateSelectedy = -1;
  809.     /**
  810.      * The day selected when the Caledar was last painted.
  811.      */
  812.     protected int lastSelectedDate = -1;
  813.     /**
  814.      * The column index of the last day in the month.
  815.      */
  816.     protected int maxMonthx = -1;
  817.     /**
  818.      * The row index of the last day in the month.
  819.      */
  820.     protected int maxMonthy = -1;
  821.     /**
  822.      * The day of the week the month starts on.
  823.      */
  824.     protected int firstDay = -1;
  825.     /**
  826.      * Internal drawing calibration value. The height of the topmost row of the Calendar, in pixels.
  827.      */
  828.     protected int topRow = -1;
  829.     /**
  830.      * Internal drawing calibration value. The height of a selected area in the Calendar, in pixels.
  831.      */
  832.     protected int selectHeight = -1;
  833.     /**
  834.      * Internal drawing calibration value.
  835.      */
  836.     protected int centerNumber = -1;
  837.     /**
  838.      * Internal drawing calibration value.
  839.      */
  840.     protected int heightAdjust = -1;
  841.     /**
  842.      * Internal drawing calibration value.
  843.      */
  844.     protected int numberAdjust = -1;
  845.  
  846.     private Action    action    = null;
  847.     private Mouse    mouse    = null;
  848.     private symantec.itools.beans.VetoableChangeSupport vetos = new symantec.itools.beans.VetoableChangeSupport(this);
  849.     private symantec.itools.beans.PropertyChangeSupport changes = new symantec.itools.beans.PropertyChangeSupport(this);
  850. }
  851.